const xmldoc = require('xmldoc')
const svgpath = require('svgpath')

// 这个方法是网上抄的
function deepClone(data) {
  var t = typeof (data), o, i, ni;
  if (t === 'array') {
    o = []
  } else if (t === 'object') {
    o = {}
  } else {
    return data
  }

  if (t === 'array') {
    for (i = 0, ni = data.length; i < ni; i++) {
      o.push(deepClone(data[i]))
    }
    return o
  } else if (t === 'object') {
    for (i in data) {
      o[i] = deepClone(data[i])
    }
    return o
  }
}

function flatPath(dom, trans) {
  const p = svgpath(dom.attr.d)
  trans.forEach(tran => p.transform(tran))
  dom.attr.d = p.round(10).toString()
  return dom
}

/**
 * transform 预计算
 * 这个方法参考了 svg-flatten 这个 npm 包
 * 将 svg 中的
 * - <use> 引用全部展开
 * - transform 属性移除, 并作用叠加到 path 上
 * 希望这样产生的 svg 可以被 manim 正确处理!
 */
module.exports = function (svg) {
  const root = new xmldoc.XmlDocument(svg)
  let defs = undefined

  function flatGroup(dom, trans) {
    const newChildren = dom.children.map(child => flat(child, trans))
    dom.children = newChildren
    if (newChildren.length > 0) {
      dom.firstChild = newChildren[0]
      dom.lastChild = newChildren[newChildren.length - 1]
    }
    return dom
  }

  function flat(dom, trans) {
    if (!dom.attr) {
      return dom
    }
    if (dom.attr.transform) {
      trans = [dom.attr.transform, ...trans]
      delete dom.attr.transform
    }

    if (dom.name === 'defs') {
      defs = dom
      return new xmldoc.XmlDocument('<defs />') // empty defs
    } else if (dom.name === 'use') {
      const id = dom.attr['xlink:href'].slice(1)
      const node = defs.childWithAttribute('id', id)
      if (!node) throw new Error('id not found:', id)
      return flat(deepClone(node), trans)
    } else if (dom.name === 'g') {
      return flatGroup(dom, trans)
    } else if (dom.name === 'svg') {
      if (dom !== root) {
        trans = [`translate(${dom.attr.x || 0},${dom.attr.y || 0})`, ...trans]
        dom.name = 'g'
        // dom.attr.transform = `translate(${-dom.attr.x || 0},${-dom.attr.y || 0})`
        delete dom.attr.width
        delete dom.attr.height
        delete dom.attr.viewBox
        delete dom.attr.x
        delete dom.attr.y
      }
      return flatGroup(dom, trans)
    } else if (dom.name === 'path') {
      return flatPath(dom, trans)
    } else if (dom.name === 'rect') {
      const { x, y, width, height } = dom.attr
      const d = 'M' + x + ' ' + y
        + 'h' + width
        + 'v' + height
        + 'h' + (-width)
        + 'z'
      return flat(new xmldoc.XmlDocument('<path d="' + d + '"></path>'), trans)
    }
    return dom
  }

  return flat(root, []).toString()
}
